﻿using ClassLibrary1.Templates;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace ClassLibrary1
{
    /// <summary>
    /// 代码生成入口类
    /// </summary>
    public static class Entrance
    {
        #region 属性
        /// <summary>
        /// 命名空间
        /// </summary>
        public static string attrnamespacestr { get; set; }
        /// <summary>
        /// 数据库路径
        /// </summary>
        private static string attrdbPath { get; set; }
        /// <summary>
        /// 数据库登陆地址
        /// </summary>
        private static string attrdbLoginName { get; set; }
        /// <summary>
        /// 数据库登陆密码
        /// </summary>
        private static string attrdbPassWord { get; set; }
        /// <summary>
        /// 需要生成的数据库名称
        /// </summary>
        public static string attrdbTableName { get; set; }
        /// <summary>
        /// 生成文件的路径
        /// </summary>
        public static string attrfilePath { get; set; }
        /// <summary>
        /// 数据库连接字符串
        /// </summary>
        private static string dbconn { get; set; }
        /// <summary>
        /// 数据库连接对象
        /// </summary>
        private static SqlConnection conn { get; set; }
        /// <summary>
        /// 
        /// </summary>
        private static SqlCommand comm { get; set; }
        #endregion

        #region 代码生成入口
        /// <summary>
        /// 代码生成开始
        /// </summary>
        public static void Start(string namespacestr, string dbPath, string dbLoginName, string dbPassWord, string dbTableName, string filePath)
        {
            #region 基本属性初始化
            Entrance.attrnamespacestr = namespacestr;
            Entrance.attrdbPath = dbPath;
            Entrance.attrdbLoginName = dbLoginName;
            Entrance.attrdbPassWord = dbPassWord;
            Entrance.attrdbTableName = dbTableName;
            Entrance.attrfilePath = filePath;
            Entrance.dbconn = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=false;User={2};Password={3};", dbPath, dbTableName, dbLoginName, dbPassWord);
            #endregion
            //获取指定数据库中所有的表名
            List<string> listEntitys = GetDbTables(dbTableName);
            if (listEntitys != null && listEntitys.Count > 0)
            {
                foreach (var TableName in listEntitys)
                {
                    //获取表中所有的字段的信息
                    var tempList = GetEntityList(TableName);
                    //生成文件
                    CreateStart(tempList);
                }
            }
        }
        #endregion

        #region 获取数据库中的所有的表名
        /// <summary>
        /// 获取数据库中的所有的表名
        /// </summary>
        /// <param name="tableName">表名</param>
        /// <returns></returns>
        public static List<string> GetDbTables(string tableName)
        {
            List<string> list = new List<string>();
            //获取到指定数据库中的所有的表
            string sql = string.Format("use {0}; select name from sysobjects where xtype in ('U','V') AND category <> 2 ORDER BY name ASC", Entrance.attrdbTableName);
            Entrance.conn = new SqlConnection(Entrance.dbconn);
            Entrance.comm = new SqlCommand(sql, Entrance.conn);
            var sda = new SqlDataAdapter(Entrance.comm);
            var ds = new DataSet();
            sda.Fill(ds);
            if (ds.Tables.Count > 0)
            {
                for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                {
                    list.Add(ds.Tables[0].Rows[i][0].ToString());
                }

            }
            return list;
        }
        #endregion

        #region 通过表名获取表中的属性列表 数据
        public static List<EntityAttrList> GetEntityList(string TableName)
        {
            List<EntityAttrList> list = new List<EntityAttrList>();
            string sql = string.Format(@"USE {0};SELECT  syscolumns.name AS ColName
       ,systypes.name AS SqlTypeName
       ,sys.extended_properties.value AS Description
       ,sysobjects.name AS TableName
       ,p2.value AS TableDescription
       ,主键=case   when   exists(SELECT   1   FROM   sysobjects   where   xtype='PK'   and   name   in   (
	SELECT   name   FROM   sysindexes   WHERE   indid   in(
	SELECT   indid   FROM   sysindexkeys   WHERE   id   =   syscolumns.id   AND   colid=syscolumns.colid
	)))   then  1   else  0   end
       ,syscolumns.colid
       FROM    syscolumns
        INNER JOIN sysobjects ON syscolumns.id = sysobjects.id
        INNER JOIN systypes ON syscolumns.xtype = systypes.xtype
        LEFT JOIN sys.extended_properties ON sys.extended_properties.major_id = syscolumns.id
                                             AND sys.extended_properties.minor_id = syscolumns.colorder
        LEFT JOIN sys.extended_properties p2 ON p2.major_id = syscolumns.id
                                                AND p2.minor_id = 0 AND p2.name = 'MS_Description'
                                                WHERE   sysobjects.name = '{1}'
        AND systypes.name <> 'sysname'
        ORDER BY syscolumns.colid ASC;", Entrance.attrdbTableName, TableName);
            Entrance.comm = new SqlCommand(sql, Entrance.conn);
            var sda = new SqlDataAdapter(Entrance.comm);
            var ds = new DataSet();
            sda.Fill(ds);
            if (ds.Tables.Count > 0)
            {
                for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
                {
                    list.Add(new EntityAttrList()
                    {
                        ColName = ds.Tables[0].Rows[i][0].ToString(),
                        SqlTypeName = ds.Tables[0].Rows[i][1].ToString(),
                        Description = ds.Tables[0].Rows[i][2].ToString(),
                        TableName = ds.Tables[0].Rows[i][3].ToString(),
                        TableDescription = ds.Tables[0].Rows[i][4].ToString(),
                        主键 = Convert.ToInt32(ds.Tables[0].Rows[i][5]),
                        TypeName = SqlTypeByType(ds.Tables[0].Rows[i][1].ToString()),
                        Sort = Convert.ToInt32(ds.Tables[0].Rows[i][6]),
                    });
                }
            }

            return list;
        }
        #endregion

        #region Sql数据类型与C#数据类型映射
        /// <summary>
        /// Sql数据类型与C#数据类型映射
        /// </summary>
        /// <param name="sqlType"></param>
        /// <returns></returns>
        public static string SqlTypeByType(string content)
        {
            Dictionary<string, string> _sqlTypeMap = new Dictionary<string, string>
        {

            {
                "nvarchar",
                "string"
            },

            {
                "varchar",
                "string"
            },

            {
                "nchar",
                "string"
            },

            {
                "ntext",
                "string"
            },

            {
                "text",
                "string"
            },

            {
                "char",
                "string"
            },

            {
                "xml",
                "string"
            },

            {
                "tinyint",
                "int"
            },

            {
                "smallint",
                "int"
            },

            {
                "bigint",
                "int"
            },

            {
                "bit",
                "bool"
            },

            {
                "real",
                "float"
            },

            {
                "float",
                "double"
            },

            {
                "smallmoney",
                "decimal"
            },

            {
                "money",
                "decimal"
            },

            {
                "decimal",
                "decimal"
            },

            {
                "numeric",
                "decimal"
            },

            {
                "smalldatetime",
                "DateTime"
            },

            {
                "datetime",
                "DateTime"
            },

            {
                "datetime2",
                "DateTime"
            },

            {
                "date",
                "DateTime"
            },

            {
                "time",
                "TimeSpan"
            },

            {
                "timestamp",
                "byte[]"
            },

            {
                "uniqueidentifier",
                "Guid"
            },

            {
                "binary",
                "byte[]"
            },

            {
                "varbinary",
                "byte[]"
            },

            {
                "image",
                "byte[]"
            }
        };
            foreach (KeyValuePair<string, string> current in _sqlTypeMap)
            {
                content = Regex.Replace(content, current.Key, current.Value);
            }
            return content;
        }
        #endregion

        #region 生成文件
        /// <summary>
        /// 生成文件
        /// </summary>
        /// <param name="list"></param>
        public static void CreateStart(List<EntityAttrList> list)
        {
            ///需要生成的文件
            ///IDAL
            ///DAL
            ///IBLL
            ///BLL
            ///Entity

            ///这里我想实现一个NB点的东西
            ///我通过反射加载Templates文件中的所有的模板类
            ///然后再调用这些模板类
            ///这样我在添加新的模板的时候，直接添加模板就行了
            ///其他逻辑的代码就不需要进行修改
            ///
            //加载程序集
            var tempAss = Assembly.Load("ClassLibrary1");
            foreach (var item in tempAss.ExportedTypes)
            {
                var temp = item;
                var tempBase = item.BaseType.ToString();
                //找到所有 TemplatesBase 的子类
                if (tempBase == "ClassLibrary1.Templates.TemplatesBase")
                {
                    //创建该子类的实例
                    object instance = tempAss.CreateInstance(temp.ToString());
                    //需要调用的方法
                    MethodInfo methodInfo = temp.GetMethod("SaveFile");
                    //构建参数列表
                    object[] obj = new object[1];
                    obj[0] = Convert.ChangeType(list, list.GetType());
                    //执行方法
                    object returnValue = methodInfo.Invoke(instance, obj);
                }
            }
        }
        #endregion
    }
}
